package com.gitee.wsl.net.client


import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.HttpResponseValidator
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.cookies.ConstantCookiesStorage
import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.http.Cookie
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

/**
 * Common service network
 */
class ServiceRequest(
    private val apiUrl: String? = null,
    private val logger: Logger? = null,
    private val debug: Boolean = true,
    cookie: Cookie? = null
) {

    private val json = Json {
        prettyPrint = true
        isLenient = true
        ignoreUnknownKeys = true
    }

    private var httpClient = getClient(
        _apiUrl = apiUrl,
        _log = logger,
        _debug = debug,
        _cookie = cookie,
    )

    /*val get = GetRequest(httpClient)
    val put = PutRequest(httpClient)
    val post = PostRequest(httpClient)
    val delete = DeleteRequest(httpClient)*/

    fun setCookie(
        cookie: Cookie
    ) {
        httpClient = getClient(
            _apiUrl = apiUrl,
            _log = logger,
            _debug = debug,
            _cookie = cookie,
        )
    }

    private fun getClient(
        _apiUrl: String? = null,
        _log: Logger? = null,
        _debug: Boolean = true,
        _cookie: Cookie? = null
    ) = HttpClient {

        expectSuccess = false

        HttpResponseValidator {
            validateResponse { response ->
                if (response.status != HttpStatusCode.OK) {
                    val error: ErrorResponse = response.body()
                    throw ResponseException(
                        code = response.status.value,
                        message = error.message,
                        validate = error.validate?.toTypedArray(),
                    )
                }
            }
        }

        if (_debug) {
            install(Logging) {
                logger = _log ?: Logger.DEFAULT
                level = LogLevel.ALL
            }
        }

        install(HttpCookies) {
            if (_cookie !== null) {
                storage = ConstantCookiesStorage(_cookie)
            }
        }

        install(DefaultRequest) {
            _apiUrl?.let {
                url(it)
            } ?: run {
                //url(AppConstants.links.API_DEBUG_URL)
            }
            contentType(ContentType.Application.Json)
        }

        install(ContentNegotiation) {
            json(json)
        }
    }
}

/**
 * Field errors
 */
@Serializable
@Suppress("ArrayInDataClass")
data class ValidateViolation(
    val filed: String,
    val errors: Array<String>
)

/**
 * Error response model
 */
@Serializable
data class ErrorResponse(
    val code: Int,
    val message: String,
    val validate: List<ValidateViolation>? = null
)

/**
 * Error response [Exception]
 */
@Suppress("ArrayInDataClass", "NON_EXPORTABLE_TYPE")
data class ResponseException(
    val code: Int,
    override val message: String,
    val validate: Array<ValidateViolation>? = null
) : RuntimeException()